// var i;
// var $resUtil = require("./ResUtil");
// var l = cc._decorator;
// var u = l.ccclass;
// var d = l.property;
// var p = l.menu;
// var h = l.requireComponent;
// var f = (function (e) {
//     function t() {
//         var t = (null !== e && e.apply(this, arguments)) || this;
//         t.itemEx = null;
//         t.itemPrefabEx = null;
//         t.scale = 1;
//         t._itemTmp = null;
//         t._isInit = !1;
//         t._listItems = new Map();
//         t._addId = 0;
//         t._excuting = new Map();
//         return t;
//     }
//     __extends(t, e);
//     t.prototype.start = function () {
//         this._init();
//     };
//     t.prototype._init = function () {
//         if (!this._isInit) {
//             this.itemEx
//                 ? ((this._itemTmp = $resUtil.ResUtil.instantiate(this.itemEx)),
//                   (this.scale = this.itemEx.scale),
//                   (this.itemEx.active = !1))
//                 : this.itemPrefabEx && (this._itemTmp = $resUtil.ResUtil.instantiate(this.itemPrefabEx));
//             this._isInit = !0;
//         }
//     };
//     t.prototype.setItemRenderer = function (e, t) {
//         this._itemRender = e;
//         this._target = t;
//     };
//     t.prototype.setNumItems = function (e, t, o, n, i) {
//         var a = this;
//         if (void 0 === t) {
//             t = 0;
//         }
//         if (void 0 === i) {
//             i = !0;
//         }
//         $resUtil.ResUtil.removeAllChildren(this.node, i);
//         this._listItems.clear();
//         this._init();
//         var r = [];
//         var c = e.length;
//         if (t <= 0) {
//             for (var l = 0; l < c; l++) {
//                 var u = this.itemGenerator(l, e[l]);
//                 r.push(u);
//             }
//             this._isInit = !0;
//             if (o) {
//                 o.call(n);
//             }
//         } else {
//             this.stopLastExecutePerFrame();
//             this._executePerFrame(
//                 this._getItemGenerator(e, c),
//                 t,
//                 function () {
//                     a._isInit = !0;
//                     if (o) {
//                         o.call(n);
//                     }
//                 },
//                 this,
//                 r
//             );
//         }
//         return r;
//     };
//     t.prototype.getItems = function () {
//         return this._listItems;
//     };
//     t.prototype._getItemGenerator = function (e, t) {
//         var o;
//         return __generator(this, function (n) {
//             switch (n.label) {
//                 case 0:
//                     t = t || e.length;
//                     o = 0;
//                     n.label = 1;
//                 case 1:
//                     if (o < t) {
//                         return [4, this.itemGenerator(o, e[o])];
//                     } else {
//                         return [3, 4];
//                     }
//                 case 2:
//                     n.sent();
//                     n.label = 3;
//                 case 3:
//                     o++;
//                     return [3, 1];
//                 case 4:
//                     return [2];
//             }
//         });
//     };
//     t.prototype.itemGenerator = function (e, t) {
//         var o = this._initItem(e);
//         if (this._itemRender) {
//             this._itemRender.call(this._target, o, t, e);
//         }
//         return o;
//     };
//     t.prototype._initItem = function (e) {
//         var t = $resUtil.ResUtil.instantiate(this._itemTmp);
//         t.name = e + "";
//         t.active = !0;
//         t.parent = this.node;
//         t.scale = this.scale;
//         this._listItems.set(e, t);
//         return t;
//     };
//     t.prototype._executePerFrame = function (e, t, o, n, i) {
//         var a = e;
//         var r = this._addId;
//         var c = this;
//         this._excuting.set(r, !0);
//         this._addId++;
//         (function e() {
//             if (!c._excuting.get(r)) {
//                 a = null;
//             }
//             if (a) {
//                 var s = new Date().getTime();
//                 for (var l = a.next(); ; l = a.next()) {
//                     if (i && l.value) {
//                         i.push(l.value);
//                     }
//                     if (null == l || l.done) {
//                         if (o) {
//                             o.call(n, i);
//                         }
//                         return void c._excuting.delete(r);
//                     }
//                     if (new Date().getTime() - s > t) {
//                         return void c.scheduleOnce(function () {
//                             e();
//                         });
//                     }
//                 }
//             }
//         })();
//     };
//     t.prototype.stopLastExecutePerFrame = function () {
//         var e = this._addId - 1;
//         this.stopExecutePerFrame(e);
//     };
//     t.prototype.stopExecutePerFrame = function (e) {
//         if (this._excuting.has(e)) {
//             this._excuting.set(e, !1);
//         }
//     };
//     __decorate([d(cc.Node)], t.prototype, "itemEx", void 0);
//     __decorate([d(cc.Prefab)], t.prototype, "itemPrefabEx", void 0);
//     __decorate(
//         [
//             d({
//                 type: cc.Float
//             })
//         ],
//         t.prototype,
//         "scale",
//         void 0
//     );
//     return __decorate([u, p("UI组件/Layout Extent"), h(cc.Layout)], t);
// })(cc.Component);
// exports.default = f;

// import { ResUtil } from './ResUtil';

// const { ccclass, property, menu, requireComponent } = cc._decorator;

// /**
//  * 建议类名：LayoutExtent
//  */
// @ccclass
// @menu("UI组件/Layout Extent")
// @requireComponent(cc.Layout)
// export default class LayoutExtent extends cc.Component {
//     /** 项目节点 */
//     @property(cc.Node)
//     public itemEx: cc.Node | null = null;

//     /** 项目预制体 */
//     @property(cc.Prefab)
//     public itemPrefabEx: cc.Prefab | null = null;

//     /** 缩放比例 */
//     @property({ type: cc.Float })
//     public scale: number = 1;

//     private _itemTmp: cc.Node | null = null;
//     private _isInit: boolean = false;
//     private _listItems: Map<number, cc.Node> = new Map();
//     private _addId: number = 0;
//     private _excuting: Map<number, boolean> = new Map();
//     private _itemRender: Function | null = null;
//     private _target: any = null;

//     start() {
//         this._init();
//     }

//     /**
//      * 初始化方法
//      * 建议方法名：initialize
//      */
//     private _init() {
//         if (!this._isInit) {
//             if (this.itemEx) {
//                 this._itemTmp = ResUtil.instantiate(this.itemEx);
//                 this.scale = this.itemEx.scale;
//                 this.itemEx.active = false;
//             } else if (this.itemPrefabEx) {
//                 this._itemTmp = ResUtil.instantiate(this.itemPrefabEx);
//             }
//             this._isInit = true;
//         }
//     }

//     /**
//      * 设置项目渲染器
//      * @param itemRender 项目渲染器函数
//      * @param target 目标对象
//      * 建议方法名：setItemRenderer
//      */
//     public setItemRenderer(itemRender: Function, target: any) {
//         this._itemRender = itemRender;
//         this._target = target;
//     }

//     /**
//      * 设置项目数量
//      * @param items 项目数组
//      * @param frameInterval 帧间隔
//      * @param callback 回调函数
//      * @param context 回调上下文
//      * @param removeAll 是否移除所有子节点
//      * 建议方法名：setNumItems
//      */
//     public setNumItems(items: any[], frameInterval: number = 0, callback?: Function, context?: any, removeAll: boolean = true): cc.Node[] {
//         ResUtil.removeAllChildren(this.node, removeAll);
//         this._listItems.clear();
//         this._init();

//         const generatedItems: cc.Node[] = [];
//         const itemCount = items.length;

//         if (frameInterval <= 0) {
//             for (let i = 0; i < itemCount; i++) {
//                 const item = this.itemGenerator(i, items[i]);
//                 generatedItems.push(item);
//             }
//             this._isInit = true;
//             if (callback) {
//                 callback.call(context);
//             }
//         } else {
//             this.stopLastExecutePerFrame();
//             this._executePerFrame(this._getItemGenerator(items, itemCount), frameInterval, () => {
//                 this._isInit = true;
//                 if (callback) {
//                     callback.call(context);
//                 }
//             }, this, generatedItems);
//         }

//         return generatedItems;
//     }

//     /**
//      * 获取项目列表
//      * @returns 项目列表
//      * 建议方法名：getItems
//      */
//     public getItems(): Map<number, cc.Node> {
//         return this._listItems;
//     }

//     /**
//      * 获取项目生成器
//      * @param items 项目数组
//      * @param itemCount 项目数量
//      * @returns 项目生成器
//      * 建议方法名：getItemGenerator
//      */
//     private *_getItemGenerator(items: any[], itemCount: number): Generator<cc.Node> {
//         itemCount = itemCount || items.length;
//         for (let i = 0; i < itemCount; i++) {
//             yield this.itemGenerator(i, items[i]);
//         }
//     }

//     /**
//      * 项目生成器
//      * @param index 项目索引
//      * @param data 项目数据
//      * @returns 生成的项目节点
//      * 建议方法名：itemGenerator
//      */
//     private itemGenerator(index: number, data: any): cc.Node {
//         const item = this._initItem(index);
//         if (this._itemRender) {
//             this._itemRender.call(this._target, item, data, index);
//         }
//         return item;
//     }

//     /**
//      * 初始化项目
//      * @param index 项目索引
//      * @returns 初始化的项目节点
//      * 建议方法名：initItem
//      */
//     private _initItem(index: number): cc.Node {
//         const item = ResUtil.instantiate(this._itemTmp);
//         item.name = index.toString();
//         item.active = true;
//         item.parent = this.node;
//         item.scale = this.scale;
//         this._listItems.set(index, item);
//         return item;
//     }

//     /**
//      * 按帧执行
//      * @param generator 项目生成器
//      * @param frameInterval 帧间隔
//      * @param callback 回调函数
//      * @param context 回调上下文
//      * @param generatedItems 生成的项目数组
//      * 建议方法名：executePerFrame
//      */
//     private _executePerFrame(generator: Generator<cc.Node>, frameInterval: number, callback: Function, context: any, generatedItems: cc.Node[]) {
//         const id = this._addId++;
//         this._excuting.set(id, true);

//         const execute = () => {
//             if (!this._excuting.get(id)) {
//                 return;
//             }

//             const startTime = Date.now();
//             let result = generator.next();

//             while (!result.done) {
//                 if (generatedItems && result.value) {
//                     generatedItems.push(result.value);
//                 }

//                 if (Date.now() - startTime > frameInterval) {
//                     this.scheduleOnce(execute);
//                     return;
//                 }

//                 result = generator.next();
//             }

//             if (callback) {
//                 callback.call(context, generatedItems);
//             }

//             this._excuting.delete(id);
//         };

//         execute();
//     }

//     /**
//      * 停止上一次按帧执行
//      * 建议方法名：stopLastExecutePerFrame
//      */
//     public stopLastExecutePerFrame() {
//         const lastId = this._addId - 1;
//         this.stopExecutePerFrame(lastId);
//     }

//     /**
//      * 停止按帧执行
//      * @param id 执行ID
//      * 建议方法名：stopExecutePerFrame
//      */
//     public stopExecutePerFrame(id: number) {
//         if (this._excuting.has(id)) {
//             this._excuting.set(id, false);
//         }
//     }
// }

import { ResourceUtil } from './ResUtil';

const { ccclass, property, menu, requireComponent } = cc._decorator;

/**
 * 建议类名：LayoutExtent
 */
@ccclass
@menu("UI组件/Layout Extent")
@requireComponent(cc.Layout)
export default class LayoutExtent extends cc.Component {
    /** 项目节点 */
    @property(cc.Node)
    public itemEx: cc.Node | null = null;

    /** 项目预制体 */
    @property(cc.Prefab)
    public itemPrefabEx: cc.Prefab | null = null;

    /** 缩放比例 */
    @property({ type: cc.Float })
    public scale: number = 1;

    // 私有变量使用私有变量前缀_,并改名以见名知义
    private _templateItem: cc.Node | null = null;
    private _initialized: boolean = false;
    private _itemsMap: Map<number, cc.Node> = new Map();
    private _currentId: number = 0;
    private _activeExecutions: Map<number, boolean> = new Map();
    private _renderFunction: Function | null = null;
    private _renderContext: any = null;

    start() {
        this._initialize();
    }

    /**
     * 初始化方法
     * 建议方法名：initialize
     */
    private _initialize() {
        if (!this._initialized) {
            if (this.itemEx) {
                this._templateItem = ResourceUtil.instantiate(this.itemEx);
                this.scale = this.itemEx.scale;
                this.itemEx.active = false;
            } else if (this.itemPrefabEx) {
                this._templateItem = ResourceUtil.instantiate(this.itemPrefabEx);
            }
            this._initialized = true;
        }
    }

    /**
     * 设置项目渲染器
     * @param itemRender 项目渲染器函数
     * @param target 目标对象
     * 建议方法名：setItemRenderer
     */
    public setItemRenderer(itemRender: Function, target: any) {
        this._renderFunction = itemRender;
        this._renderContext = target;
    }

    /**
     * 设置项目数量
     * @param items 项目数组
     * @param frameInterval 帧间隔
     * @param callback 回调函数
     * @param context 回调上下文
     * @param removeAll 是否移除所有子节点
     * 建议方法名：setNumItems
     */
    public setNumItems(items: any[], frameInterval: number = 0, callback?: Function, context?: any, removeAll: boolean = true): cc.Node[] {
        ResourceUtil.removeAllChildrenAndRelease(this.node, removeAll);
        this._itemsMap.clear();
        this._initialize();

        const generatedItems: cc.Node[] = [];
        const itemCount = items.length;

        if (frameInterval <= 0) {
            for (let i = 0; i < itemCount; i++) {
                const item = this._generateItem(i, items[i]);
                generatedItems.push(item);
            }
            this._initialized = true;
            if (callback) {
                callback.call(context);
            }
        } else {
            this.stopLastExecutePerFrame();
            this._executePerFrame(this._getItemGenerator(items, itemCount), frameInterval, () => {
                this._initialized = true;
                if (callback) {
                    callback.call(context);
                }
            }, this, generatedItems);
        }

        return generatedItems;
    }

    /**
     * 获取项目列表
     * @returns 项目列表
     * 建议方法名：getItems
     */
    public getItems(): Map<number, cc.Node> {
        return this._itemsMap;
    }

    /**
     * 获取项目生成器
     * @param items 项目数组
     * @param itemCount 项目数量
     * @returns 项目生成器
     * 建议方法名：getItemGenerator
     */
    private *_getItemGenerator(items: any[], itemCount: number): Generator<cc.Node> {
        itemCount = itemCount || items.length;
        for (let i = 0; i < itemCount; i++) {
            yield this._generateItem(i, items[i]);
        }
    }

    /**
     * 项目生成器
     * @param index 项目索引
     * @param data 项目数据
     * @returns 生成的项目节点
     * 建议方法名：itemGenerator
     */
    private _generateItem(index: number, data: any): cc.Node {
        const item = this._createItem(index);
        if (this._renderFunction) {
            this._renderFunction.call(this._renderContext, item, data, index);
        }
        return item;
    }

    /**
     * 初始化项目
     * @param index 项目索引
     * @returns 初始化的项目节点
     * 建议方法名：initItem
     */
    private _createItem(index: number): cc.Node {
        const item = ResourceUtil.instantiate(this._templateItem);
        item.name = index.toString();
        item.active = true;
        item.parent = this.node;
        item.scale = this.scale;
        this._itemsMap.set(index, item);
        return item;
    }

    /**
     * 按帧执行
     * @param generator 项目生成器
     * @param frameInterval 帧间隔
     * @param callback 回调函数
     * @param context 回调上下文
     * @param generatedItems 生成的项目数组
     * 建议方法名：executePerFrame
     */
    private _executePerFrame(generator: Generator<cc.Node>, frameInterval: number, callback: Function, context: any, generatedItems: cc.Node[]) {
        const id = this._currentId++;
        this._activeExecutions.set(id, true);

        const execute = () => {
            if (!this._activeExecutions.get(id)) {
                return;
            }

            const startTime = Date.now();
            let result = generator.next();

            while (!result.done) {
                if (generatedItems && result.value) {
                    generatedItems.push(result.value);
                }

                if (Date.now() - startTime > frameInterval) {
                    this.scheduleOnce(execute);
                    return;
                }

                result = generator.next();
            }

            if (callback) {
                callback.call(context, generatedItems);
            }

            this._activeExecutions.delete(id);
        };

        execute();
    }

    /**
     * 停止上一次按帧执行
     * 建议方法名：stopLastExecutePerFrame
     */
    public stopLastExecutePerFrame() {
        const lastId = this._currentId - 1;
        this.stopExecutePerFrame(lastId);
    }

    /**
     * 停止按帧执行
     * @param id 执行ID
     * 建议方法名：stopExecutePerFrame
     */
    public stopExecutePerFrame(id: number) {
        if (this._activeExecutions.has(id)) {
            this._activeExecutions.set(id, false);
        }
    }
}